package chess

import (
	"fmt"
	"runtime/debug"

	"gddgame.cc/galaxy/asteroid"
	"gddgame.cc/galaxy/asteroid/cluster"
	"gddgame.cc/galaxy/common/feature/orbit"
	"gddgame.cc/galaxy/common/feature/orbit/chess_protocol"
	"gddgame.cc/galaxy/satellite/formula/comet"
	"gddgame.cc/galaxy/utils"
	"gddgame.cc/galaxy/utils/def"
	"gddgame.cc/galaxy/utils/serialize"
)

const (
	_ comet.DE = iota
	Register
	UnRegister
	SwitchMap
	SwitchLevel

	OnNotice
	OnSwitchMap
	OnSwitchLevel
)

const (
	Receive           comet.DE = 100
	CreateDataDetail  comet.DE = 101
	UpdateDataDetail  comet.DE = 102
	MoveDataDetail    comet.DE = 103
	DestroyDataDetail comet.DE = 104
	//CreateNodeDetail  comet.DE = 105
	UpdateNodeDetail  comet.DE = 106
	SendMessageDetail comet.DE = 110

	FromMoveDataDetail comet.DE = 113
)

type BlockDelegate interface {
	//asteroid.Meeting

	NeedForward() bool
	CanSend(r uint16) bool

	GetClient(position []byte) *cluster.Client
	GetClientWithSub(sub string) *cluster.Client
	GetClientWithSubLevel(sub string, level orbit.Point) *cluster.Client
	GetSub(position []byte, log bool) string
	GetSubWithLevel(position []byte, level orbit.Point, log bool) string

	GetBlock() *orbit.BlockLocation
	GetMap() string
}

type BlockInstance struct {
	Datas    map[byte]*DataManager
	Location string

	rMap map[uint16][]*cluster.Client

	block    *orbit.BlockLocation
	delegate BlockDelegate
	node     cluster.Node
}

func (instance *BlockInstance) Bytes(d serialize.IStruct) []byte {
	b, err := d.MarshalMsg(nil)
	if err != nil {
		fmt.Println("block instance", err)
		debug.PrintStack()
	}
	return b
}

func (instance *BlockInstance) Forward(position []byte, r uint16, detail comet.DE, data []byte, t int64) {
	if !instance.delegate.NeedForward() {
		return
	}
	if r == 0 {
		return
	}
	if clients, ok := instance.rMap[r]; !ok {
		clients = []*cluster.Client{}
		for i := uint(DefaultLevel + 1); i < 16; i++ {
			if utils.IncludeInt(int(r), i) {
				c := instance.delegate.GetSubWithLevel(position, orbit.Point(i), false)
				cc := instance.delegate.GetClientWithSubLevel(c, orbit.Point(i))
				//fmt.Println("forward", instance.block, r, i, c, detail)
				clients = append(clients, cc)
				cc.Emit().List(data, t).Send(detail)
			}
		}
		instance.rMap[r] = clients
	} else {
		for _, cc := range clients {
			cc.Emit().List(data, t).Send(detail)
		}
	}
}

//
//func (instance *BlockInstance) CreateNode(data []byte, t int64) {
//	var createNode chess_protocol.Data
//	slot := serialize.GetSlot()
//	_ = slot.Set(&createNode)
//	if err := slot.Unmarshal(data); err != nil {
//		slot.Release()
//		fmt.Println("createnode", err)
//		return
//	}
//	//fmt.Println("create node", createNode)
//	instance.Nodes.Add(&createNode)
//	instance.Forward(createNode.Position, createNode.Range, CreateNodeDetail, data, t)
//	slot.Release()
//}
func (instance *BlockInstance) UpdateNode(data []byte, t int64) {
	//fmt.Println("update node")
	var updateNode chess_protocol.UpdateNode
	slot := serialize.GetSlot()
	_ = slot.Set(&updateNode)
	if err := slot.Unmarshal(data); err != nil {
		slot.Release()
		fmt.Println("updatenode", err)
		return
	}
	dataManager, ok := instance.Datas[updateNode.DataType]
	if !ok {
		dataManager = NewDataManager(updateNode.DataType, instance.block)
		instance.Datas[updateNode.DataType] = dataManager
	}
	if updateNode.Data.Message != nil && !instance.delegate.CanSend(updateNode.Data.Message.Range) {
		updateNode.Data.Message = nil
	}
	//fmt.Println("update node", updateNode)
	d := dataManager.UpdateNode(&updateNode)

	instance.Forward(d.Data.Position, d.Data.Range, UpdateNodeDetail, data, t)
	slot.Release()
	instance.node.Broadcast().List(OnSingleNode, data, t).Send(OnNotice)
}
func (instance *BlockInstance) CreateData(data []byte, t int64) {
	var createData chess_protocol.SingleData
	slot := serialize.GetSlot()
	_ = slot.Set(&createData)
	if err := slot.Unmarshal(data); err != nil {
		slot.Release()
		fmt.Println("createdata", err, data, createData)
		return
	}
	//fmt.Println("create", createData)
	dataManager, ok := instance.Datas[createData.DataType]
	if !ok {
		dataManager = NewDataManager(createData.DataType, instance.block)
		instance.Datas[createData.DataType] = dataManager
	}
	dataManager.Add(&createData)
	//fmt.Println("create data", singleData)
	//fmt.Println("create data", createData.DataType, createData.Id, createData.Data, instance.delegate.GetMap(), instance.delegate.GetBlock())
	instance.Forward(createData.Data.Position, createData.Data.Range, CreateDataDetail, data, t)
	instance.node.Broadcast().List(OnCreateData, data, t).Send(OnNotice)
	slot.Release()
}
func (instance *BlockInstance) UpdateData(data []byte, t int64) {
	var updateData chess_protocol.UpdateData
	slot := serialize.GetSlot()
	_ = slot.Set(&updateData)
	if err := slot.Unmarshal(data); err != nil {
		slot.Release()
		fmt.Println("updatedata", err)
		return
	}
	dataManager, ok := instance.Datas[updateData.DataType]
	if !ok {
		dataManager = NewDataManager(updateData.DataType, instance.block)
		instance.Datas[updateData.DataType] = dataManager
	}
	//var d *Data
	if p := dataManager.InMoving(updateData.Id); p != nil {
		c := instance.delegate.GetClient(p)
		c.Emit().List(data, t).Send(UpdateDataDetail)
	} else {
		//fmt.Println("update data", updateData, instance.delegate.GetBlock(), updateData.Data.Message)
		if updateData.Data.Message != nil && !instance.delegate.CanSend(updateData.Data.Message.Range) {
			updateData.Data.Message = nil
		}
		d, index := dataManager.Update(updateData.Id, &updateData.Data)
		data = instance.Bytes(&updateData)
		if d == nil {
			fmt.Println("update data", updateData, index, instance.delegate.GetBlock())
		} else {
			instance.Forward(d.Data.Position, d.Data.Range, UpdateDataDetail, data, t)
		}
		//fmt.Println("update data", updateData, index, instance.delegate.GetBlock(), updateData.Data.Message)
		instance.node.Broadcast().List(OnUpdateData, data, t).Send(OnNotice)
	}
	slot.Release()
}

func (instance *BlockInstance) MoveData(data []byte, t int64) {
	var moveData chess_protocol.MoveData
	slot := serialize.GetSlot()
	_ = slot.Set(&moveData)
	if err := slot.Unmarshal(data); err != nil {
		slot.Release()
		fmt.Println("movedata", err)
		return
	}
	//fmt.Println("move", moveData, instance.delegate.GetBlock())
	dataManager, ok := instance.Datas[moveData.DataType]
	if !ok {
		dataManager = NewDataManager(moveData.DataType, instance.block)
		instance.Datas[moveData.DataType] = dataManager
	}

	if p := dataManager.InMoving(moveData.Id); p != nil {
		//fmt.Println("move 2", p)
		c := instance.delegate.GetClient(p)
		c.Emit().List(data, t).Send(MoveDataDetail)
		goto Release
	} else {
		if moveData.Data.Message != nil && !instance.delegate.CanSend(moveData.Data.Message.Range) {
			moveData.Data.Message = nil
		}
		d, index := dataManager.UpdateMove(&moveData)
		if d == nil {
			fmt.Println("move data miss", moveData, index, instance.delegate.GetMap(), instance.delegate.GetBlock())
		} else {
			fmt.Println("move data", moveData.DataType, moveData.Id, moveData.Current, moveData.Position, instance.delegate.GetBlock(), moveData.Data.Message, t)
			location := instance.delegate.GetSub(moveData.Current, false)
			if location != instance.Location {
				dataManager.Move(d.Id)
				//fmt.Println("move data", moveData)
				c := instance.delegate.GetClientWithSub(location)
				b, err := d.MarshalMsg(nil)
				if err != nil {
					fmt.Println("move data", err)
					slot.Release()
					return
				}
				//fmt.Println("move send", moveData, d.Data.Position, instance.block)
				c.Emit().List(b, t).CallbackTo(func(success bool, dd interface{}) {
					dataManager.Remove(moveData.Id)
				}).Send(FromMoveDataDetail)
			}
			instance.Forward(moveData.Position, d.Data.Range, MoveDataDetail, data, t)
		}
		instance.node.Broadcast().List(OnMoveData, instance.Bytes(&moveData), t).Send(OnNotice)
	}
Release:
	slot.Release()
}

func (instance *BlockInstance) DestroyData(data []byte, t int64) {
	var destroyData chess_protocol.DestroyData
	slot := serialize.GetSlot()
	_ = slot.Set(&destroyData)
	if err := slot.Unmarshal(data); err != nil {
		slot.Release()
		fmt.Println("destroydata", err)
		return
	}
	dataManager, ok := instance.Datas[destroyData.DataType]
	if !ok {
		slot.Release()
		return
	}
	//fmt.Println("destroy", destroyData.DataType, destroyData.Id, instance.delegate.GetMap(), instance.delegate.GetBlock(), t)
	if p := dataManager.InMoving(destroyData.Id); p != nil {
		c := instance.delegate.GetClient(p)
		c.Emit().List(data, t).Send(DestroyDataDetail)
	} else {
		d := dataManager.Delete(destroyData.Id)
		if d == nil {
			fmt.Println("delete miss", destroyData.Id)
			slot.Release()
			return
		}
		instance.Forward(d.Data.Position, d.Data.Range, DestroyDataDetail, data, t)
		instance.node.Broadcast().List(OnDestroyData, data, t).Send(OnNotice)
	}
	slot.Release()
}

func (instance *BlockInstance) FromMoveData(data []byte, t int64) bool {
	//fmt.Println("from move", instance.delegate.GetBlock(), t)
	var d chess_protocol.SingleData
	slot := serialize.GetSlot()
	_ = slot.Set(&d)
	if err := slot.Unmarshal(data); err != nil {
		slot.Release()
		fmt.Println("from", err)
		return false
	}
	//fmt.Println("from move", d.DataType, d.Id, instance.delegate.GetMap(), instance.delegate.GetBlock(), t)
	dataManager, ok := instance.Datas[d.DataType]
	if !ok {
		dataManager = NewDataManager(d.DataType, instance.block)
		instance.Datas[d.DataType] = dataManager
	}

	dataManager.SetIndex(&d)
	instance.node.Broadcast().List(OnCreateData, data, t).Send(OnNotice)
	slot.Release()
	return true
}

func (instance *BlockInstance) SendMessage(data []byte, t int64) {
	var d chess_protocol.SendMessage
	slot := serialize.GetSlot()
	_ = slot.Set(&d)
	if err := slot.Unmarshal(data); err != nil {
		slot.Release()
		return
	}
	dataManager, ok := instance.Datas[d.DataType]
	if !ok {
		dataManager = NewDataManager(d.DataType, instance.block)
		instance.Datas[d.DataType] = dataManager
	}
	if p := dataManager.InMoving(d.Id); p != nil {
		c := instance.delegate.GetClient(p)
		c.Emit().List(data, t).Send(SendMessageDetail)
	} else {
		dd := dataManager.Message(&d)
		if dd == nil || dd.Data == nil {
			switch iid := d.Id.(type) {
			case []byte:
				// position事件可以直接发送
				instance.Forward(iid, d.Message.Range, SendMessageDetail, data, t)
			}
		} else {
			instance.Forward(dd.Data.Position, d.Message.Range, SendMessageDetail, data, t)
		}
		//fmt.Println("send message", d.DataType, d.Id, instance.delegate.GetBlock(), d.Message, t)
		instance.node.Broadcast().List(OnMessage, data, t).Send(OnNotice)
	}
	slot.Release()
}

type BlockService struct {
	def.Logger

	GetSocketClient func(socket *comet.Socket) *Client
}

func (cs *BlockService) InitService(node cluster.Node, instance *BlockInstance, delegate BlockDelegate) {
	instance.delegate = delegate
	instance.node = node
	instance.rMap = map[uint16][]*cluster.Client{}
	node.On(cluster.JoinEvent, func(message asteroid.Message) {
		message.Response(true)
		//if len(instance.Nodes.Nodes) > 0 {
		//	//for _, data := range instance.Nodes.Nodes {
		//	//	fmt.Println("init", data.Props)
		//	//}
		//	node.Broadcast().TargetId(message.GetOrigin()).List(OnBatchNode, instance.Bytes(&instance.Nodes.Nodes), true).Send(OnNotice)
		//}
		for _, dm := range instance.Datas {
			if len(dm.Datas) > len(dm.Idle) {
				for _, data := range dm.Datas {
					if data != nil {
						fmt.Println("init", data.DataType, data.Id, data.Data)
					}
				}
				//fmt.Println("join", instance.block, dm.DataType, len(dm.Datas))
				node.Broadcast().TargetId(message.GetOrigin()).List(OnBatchData, instance.Bytes(&dm.Datas), true).Send(OnNotice)
			}
		}
	})
	node.On(cluster.LeaveEvent, func(message asteroid.Message) {
		fmt.Println("leave", instance.block)
		message.Response(true)
	})
	node.On(Receive, func(message asteroid.Message) {
		var data chess_protocol.BlockData
		slot := serialize.GetSlot()
		message.UnSerialize(slot)
		if err := slot.Set(&data); err != nil {
			slot.Release()
			fmt.Println("receive", err)
			return
		}

		//v := time.Now().UnixNano()/1e6 - data.Time
		//if v > 1000 {
		//	fmt.Println("receive end", data.Time, "big", v)
		//}
		slot.Release()
		//fmt.Println("detail", data.Detail)
		for _, detail := range data.Detail {
			switch detail.Type {
			case chess_protocol.CreateDataDetail:
				instance.CreateData(detail.Content, data.Time)
			case chess_protocol.UpdateDataDetail:
				instance.UpdateData(detail.Content, data.Time)
			case chess_protocol.MoveDataDetail:
				instance.MoveData(detail.Content, data.Time)
			case chess_protocol.DestroyDataDetail:
				instance.DestroyData(detail.Content, data.Time)
			//case chess_protocol.CreateNodeDetail:
			//	instance.CreateNode(detail.Content, data.Time)
			case chess_protocol.UpdateNodeDetail:
				instance.UpdateNode(detail.Content, data.Time)
			case chess_protocol.SendMessageDetail:
				instance.SendMessage(detail.Content, data.Time)
			}
		}
	})
	//node.On(CreateNodeDetail, func(message asteroid.Message) {
	//	b := serialize.GetBag()
	//	message.UnSerialize(b)
	//	instance.CreateNode(b.ReadBytes(0), b.ReadInt64(1))
	//	b.Release()
	//})
	node.On(UpdateNodeDetail, func(message asteroid.Message) {
		b := serialize.GetBag()
		message.UnSerialize(b)
		instance.UpdateNode(b.ReadBytes(0), b.ReadInt64(1))
		b.Release()
	})
	node.On(CreateDataDetail, func(message asteroid.Message) {
		b := serialize.GetBag()
		message.UnSerialize(b)
		instance.CreateData(b.ReadBytes(0), b.ReadInt64(1))
		b.Release()
	})
	node.On(UpdateDataDetail, func(message asteroid.Message) {
		b := serialize.GetBag()
		message.UnSerialize(b)
		instance.UpdateData(b.ReadBytes(0), b.ReadInt64(1))
		b.Release()
	})
	node.On(MoveDataDetail, func(message asteroid.Message) {
		b := serialize.GetBag()
		message.UnSerialize(b)
		instance.MoveData(b.ReadBytes(0), b.ReadInt64(1))
		b.Release()
	})
	node.On(FromMoveDataDetail, func(message asteroid.Message) {
		b := serialize.GetBag()
		message.UnSerialize(b)
		message.Response(instance.FromMoveData(b.ReadBytes(0), b.ReadInt64(1)))
		b.Release()
	})
	node.On(SendMessageDetail, func(message asteroid.Message) {
		b := serialize.GetBag()
		message.UnSerialize(b)
		instance.SendMessage(b.ReadBytes(0), b.ReadInt64(1))
		b.Release()
	})
	node.On(DestroyDataDetail, func(message asteroid.Message) {
		b := serialize.GetBag()
		message.UnSerialize(b)
		instance.DestroyData(b.ReadBytes(0), b.ReadInt64(1))
		b.Release()
	})
}
func (cs *BlockService) InitClient(client *cluster.Client) {
	//client.Filter("ping", func(message *asteroid.Message, member def.Member) {
	//	socket := member.(*comet.Socket)
	//	cc := cs.GetSocketClient(socket)
	//	cc.NoticeBatch.Flush(cc, OnNotice)
	//})
	client.Filter(OnNotice, func(message asteroid.Message, member def.Member) {
		socket := member.(*comet.Socket)
		cc := cs.GetSocketClient(socket)
		list := message.List()
		flush := int64(0)
		if len(list) > 2 {
			flush = utils.ToInt64(list[2])
		}

		//fmt.Println("notice", len(list))
		cc.NoticeBatch.Write(cc, OnNotice, list)
		if flush > 0 {
			cc.NoticeBatch.Flush(cc, OnNotice, true)
		}
	})
	client.Filter(asteroid.DefaultFilter, func(message asteroid.Message, member def.Member) {
		socket := member.(*comet.Socket)
		d, _ := message.Payload()

		socket.Send(comet.DE(utils.ToByte(message.GetEvent())), d)
	})
}
